시계열 데이터들을 다루다보면 간혹 시간 데이터를 다루는 경우가 있다. 그런데, 그냥 날짜만 표시하는 것이 아니라 여러 날짜들 간의 간격을 계산해야 하는 경우도 있고, 심지어 어떨 때는 서머타임이나 윤년을 적용하지 않아 계산이 틀리는 경우도 발생한다. MATLAB에서는 “datetime” 이라는 데이터 형식을 지원하고 있어 시간 데이터를 쉽게 다룰 수 있게 해주고 있다. 또, 테이블의 확장판인 “timetable” 형식을 지원하고 있어 시간 데이터가 포함된 테이블을 다룰 때 유용하게 쓸 수 있다.

날짜와 시간 다루기

datetime형 배열 정의하기

MATLAB에서는 날짜/시간 정보를 담고 있는 datetime형 배열을 아래와 같이 생성할 수 있다. datetime형 배열로는 특정 시점에 대한 정보를 다루게 된다.

t = datetime(2022, 12, 28, 16:17, 0, 0)
  1×2 datetime array

   2022-12-28 16:00:00   2022-12-28 17:00:00

참고로 datetime형 배열의 아이콘은 아래와 같다.


위의 스크립트에서 “t”라는 변수를 생성할 때 “16:17” 과 같이 콜론 연산자를 이용해 오후 4시, 5시 데이터를 한꺼번에 생성한 것을 볼 수 있다.

만약, 시간을 표기하는 방법이 문자열로 구성되어 있다면 아래와 같이 문자열 뒤에 어떤 포맷으로 시간이 표기되었는지 명시해줄 수 있다.

t1 = datetime('28/12/2022', 'InputFormat', 'dd/MM/yyyy')
t2 = datetime('2022年 12月 28日', 'InputFormat', 'yyyy年 MM月 dd日')
t1 = 

  datetime

   2022-12-28


t2 = 

  datetime

   2022-12-28

이번에는 변수 “t”에서 얻어낼 수 있는 값들이 어떤 것이 있는지 알아보자. Command Window에서 “t.”이라고 입력한 뒤 탭키를 눌러보면 아래와 같이 “t” 변수가 가지고 있는 속성값들을 확인할 수 있다.


이 속성값들을 살펴보면 연, 월, 일, 시, 분, 초에 대한 정보를 따로 얻어낼 수 있다는 점을 짐작할 수 있다. 실제로 “t.Hour” 이라고 입력하면 시간 값들만 얻어올 수 있다.

t = datetime(2022, 12, 28, 16:17, 0, 0);
t.Hour
ans =

    16    17

또, 이 속성값은 아래와 같이 수정해볼 수도 있다. “t”의 시간을 한 시간씩 더하려고 하면 아래와 같이 처리할 수 있다.

t = datetime(2022, 12, 28, 16:17, 0, 0);
t.Hour = t.Hour + 1
t = 

  1×2 datetime array

   2022-12-28 17:00:00   2022-12-28 18:00:00

datetime형 배열 활용하기

그래프 그리기

그래프를 그릴 때, 날짜/시간 별로 데이터값이 바뀌는 경우가 종종 있다. 가령 주식 데이터를 다룬다고 하면 날짜별로 바뀌는 주식값들을 그려야 할 수 있다. 아래의 데이터를 다운 받아서 그래프를 그려보도록 하자.

데이터 다운로드

clear; close all; clc;
load("datetime_and_stock_AAPL_AMZN.mat")
plot(Date, AAPL);
hold on;
plot(Date, AMZN);
legend('AAPL', 'AMZN', 'location', 'best');
grid on;
ylabel('$')

위 스크립트에서 변수 “Date” 는 datetime형 배열로 날짜값이 들어가있다. 이렇게 그래프를 그릴 때 datetime형 배열을 활용하면 시간에 따라 값이 바뀌는 데이터들을 표현할 때 자연스럽게 표현할 수 있다.


날짜 비교

datetime 타입 변수를 이용하면 날짜 및 시간 비교를 쉽게 수행할 수 있다. 예를 들어 아래와 같이 날짜/시간 비교를 수행할 수 있다. 가령 1989년 1월~12월 15일과 1989년 6월 6일을 비교할 때 사용할 수 있다.

dt1 = datetime(1989, 1:12, 15);
dt2 = datetime(1989, 6, 6);
dt1 > dt2
ans =

  1×12 logical array

   0   0   0   0   0   1   1   1   1   1   1   1

솔직히 말해서 위의 예시는 아주 간단한 비교라고 할 수 있지만, 만약 Time Zone이 다른 경우라면 쉽게 계산할 수 없다. 가령, 한국의 2022년 12월 28일 22시 부터 29일 새벽 3시와 미국 동부 시간 2022년 12월 28일 오후 12시를 비교했을 때 과연 어떤 시간이 더 빠른 시간인지 계산하라고 하면… 쉽지는 않은 계산이다.

dt1 = [datetime(2022, 12, 28, 22:27, 0, 0, 'TimeZone', 'Asia/Seoul')];
dt2 = datetime(2022, 12, 28, 12, 0, 0, 'TimeZone', 'America/New_York');
dt1 > dt2
ans =

  1×7 logical array

   0   0   0   0   0   0   1

만약 ‘TimeZone’ 뒤에 들어갈 옵션이 어떤 것이어야 하는지 궁금하다면, 아래와 같이 “timezones” 함수 안에 대륙 이름을 입력하면 지역 별 표준시간대 목록을 확인할 수 있다.

timezones('Asia')


요일 알아내기

요일을 알아내려면 “day” 함수에서 ‘name’ 옵션을 이용하면 된다. 예를 들면 아래와 같다.

dt = datetime(2022, 12, 28);
day(dt, 'name')
ans =

  1×1 cell array

    {'수요일'}

시간 간격 다루기(duration, calendarDuration)

날짜/시간 데이터를 다룰 때 필요한 기능 중 하나는 시간 간격 계산이다. 시간 간격 계산에 사용되는 시간 길이 관련 변수 타입은 두 가지 인데 하나는 duration이고 또 하나는 calendarDuration이다.


duration은 “고정 길이 단위의 시간”을 처리하는데 쓰이고 calendarDuration은 “가변 길이 달력 단위의 시간 길이”를 다루는데 쓰인다. 그러면 굳이 왜 “가변 길이” 같은게 필요하냐고 생각할 수도 있지만, “가변 길이 달력 단위 시간”이라는 것은 매 달이 28, 29, 30, 31일과 같이 한 달의 길이가 다르고, 서머 타임 변동까지 고려해 시간을 계산해주는 기능을 포함한다는 의미이다. 다시 말해 duration에서는 60초 = 1분, 60분 = 1 시간과 같이 수치적으로 고정된 시간 단위 간 변환을 수행하는 반면 calendarDuration은 해마다 바뀌는 달력 상 시간 길이를 고려한다는 차이점이 있는 것이다. 또, calendarDuration은 “달력 단위의 시간 길이”를 다루고 있기 때문에 기본적으로 일(day) 단위 이상의 시간 길이에 대해 특화되어 있다는 차이도 있다.

datetime과 duartion(혹은 calendarDuration)의 차이점은 datetime은 특정 시점에 대한 정보를 다루는 것에 반해 duration(혹은 calendarDuration)은 어느 시점부터 어느 시점까지의 시간 경과량을 다룬다는 점이다. 아래 그림을 보면 두 시점(2002년 12월 15일, 2022년 12월 29일)에 대한 정보는 datetime형 배열에 담을 수 있다는 점을 알 수 있다. 그리고 두 시점 간의 경과일을 계산해보면 7319일임을 알 수 있는데 이 정보는 duration형 배열에 담게 되는 것이다.

여기서, 두 시점 간의 날짜 간격을 잘 생각해보면 정확히 20년 14일이라는 것을 암산해볼 수 있는데, 이 정보가 calendarDuration형 배열에 담기는 것이다. 여기서 잘 보면, 7319일을 연 단위로 환산하면 20.0387년인것을 알 수 있는데, 0.0387 * 365일은 14일로 정확히 떨어지지 않는다는 것 또한 알 수 있다1. 이것은 2002년과 2022년 사이에 5번의 윤년이 있기 때문이다.


duration형 배열

years, days, hours, minutes, seconds, milliseconds 함수를 이용해서 duration형 배열을 생성할 수 있다.

D = hours(1:3)
D = 

  1×3 duration array

   1 hr   2 hr   3 hr

혹은 duration 함수를 이용해 시, 분, 초를 각각 명시할 수 있다.

D = duration(1, 5:5:15, 0)
D = 

  1×3 duration array

   01:05:00   01:10:00   01:15:00

duration형 배열끼리 더하거나 뺄 수 있으며, datetime형 배열에 duration형 배열을 더할 수 있다.

hours(1:2) + minutes(30)

dt = datetime(2024, 2, 28, 0, 0, 0);
dt + hours(1)
ans = 

  1×2 duration array

   1.5 hr   2.5 hr


ans = 

  datetime

   2024-02-28 01:00:00

또, datetime형 변수끼리 빼주면 duration형으로 출력된다. 참고로 calendarDuration 타입으로 시간 간격을 계산하려면 “between” 함수를 써주어야 한다. 아래 예시에서는 “시간” 단위로 duration형 결과물을 얻었는데, 여기서 “days(t2-t1)”과 같이 입력해주거나 “years(t2-t1)”과 같이 입력해주면 일 혹은 연 단위로 결과물을 변환시킬 수 있다.

t1 = datetime(2002, 12, 15);
t2 = datetime(2022, 12, 29);
t2 - t1
between(t1, t2)

ans = 

  duration

   175656:00:00


ans = 

  calendarDuration

   20y 14d

calendarDuration형 배열

calyears, calquarters, calmonths, calweeks, caldays 함수를 이용해서 시간 단위를 지정한 calendarDuration형 배열을 생성할 수 있다. 가령 아래와 같이 0~3달 길이에 해당하는 배열을 생성할 수 있다.

M = calmonths(0:3)
M = 

  1×4 calendarDuration array

   0mo   1mo   2mo   3mo

또, duration 변수를 다룰 때와 유사하게 “calendarDuration” 함수를 이용해 연수, 월수, 일수를 직접 지정하여 calendarDuration형 배열을 생성할 수도 있다.

L = calendarDuration(10, 2, 5) % 10년, 2개월, 5일
L = 

  calendarDuration

   10y 2mo 5d

calendarDuration형 배열끼리 더하거나 뺄 수 있으며, datetime형 배열에 calendarDuration형 배열을 더할 수 있다.

L1 = calendarDuration(10, 2, 5); % 10년, 2개월, 5일
L2 = calendarDuration(5, 1, 30); % 5년 ,1개월, 30일
L = L1 + L2

dt = datetime(2002, 12, 15);
dt + L
L = 

  calendarDuration

   15y 3mo 35d


ans = 

  datetime

   2018-04-19

특정 날짜들 간의 시간 간격이 가변 달력 단위 시간으로 며칠인지 계산하기 위해서는 “between” 함수를 사용할 수 있다.

t1 = datetime(2002, 12, 15);
t2 = datetime(2022, 12, 29);
between(t1, t2)
ans = 

  calendarDuration

   20y 14d

타임테이블

타임테이블형 배열(혹은 타임테이블)은 앞서 서로 다른 형식의 데이터 관리(셀, 구조체, 테이블) 편에서 배운 테이블형 배열의 일종으로써 각 행에 시간값이 연결되어 있다. 다시 말하면, “RowName”이 “datetime”형 배열인 테이블이라고 할 수 있겠다.

타임테이블형 배열의 생성

타임테이블을 생성하기 위해선 테이블을 생성할 때와 마찬가지로 각 변수들을 입력으로 사용하되, 첫 번째 입력 변수가 datetime형 혹은 duration형인 경우 이것을 ‘RowTime’에 해당하는 시간에 관한 행변수로 인식한다.

예를 들어 아래와 같이 생성할 수 있다.

Time = datetime({'2022-12-15 10:03:24'; '2022-12-15 10:58:44'; '2022-12-15 12:03:02'});
Temp = [38; 36.5; 37];
Height = [100; 120; 105];
Foo = [10; 15; 32];

TT = timetable(Time, Temp, Height, Foo)
TT =

  3×3 timetable

           Time            Temp    Height    Foo
    ___________________    ____    ______    ___

    2022-12-15 10:03:24      38     100      10 
    2022-12-15 10:58:44    36.5     120      15 
    2022-12-15 12:03:02      37     105      32 

참고로 타임테이블의 아이콘은 아래와 같다.


타임테이블형 배열의 참조 방법

타임테이블형 배열의 참조 방법은 테이블형 배열의 참조 방법과 거의 유사하다. 다시 말해, 소괄호 ()를 이용해 인덱싱하면 테이블 덩어리를 가져올 수 있고, 중괄호 {}를 이용하면 테이블의 내용만 가져올 수 있다.


그런데, 타임테이블은 항상 행변수, 즉 RowTime이 datetime형 배열 혹은 duration형 배열로 되어 있기 때문에 특정 시간 조건을 만족하는 경우의 indexing에 대해서도 생각해볼 필요가 있다. 이를 이용하기 위해서 datetime 변수 간에 비교 연산자를 이용하거나 “timerange” 함수를 이용할 수 있다. 예를 들어, 위의 타임테이블 TT에서 2022년 12월 15일 오전 10시에서 오전 11시 사이에 해당하는 테이블만 얻고 싶다면 다음과 같이 인덱싱할 수 있다.

Time = datetime({'2022-12-15 10:03:24'; '2022-12-15 10:58:44'; '2022-12-15 12:03:02'});
Temp = [38; 36.5; 37];
Height = [100; 120; 105];
Foo = [10; 15; 32];

TT = timetable(Time, Temp, Height, Foo);

%% First Way
rowTime = TT.Properties.RowTimes;
startTime = datetime(2022, 12, 15, 10, 0, 0);
endTime = datetime(2022, 12, 15, 12, 0, 0);
idx = (rowTime >= startTime) ...
    & (rowTime < endTime);

TT(idx,:)

%% Second Way
idx = timerange(startTime, endTime);
TT(idx,:)
ans =

  2×3 timetable

           Time            Temp    Height    Foo
    ___________________    ____    ______    ___

    2022-12-15 10:03:24      38     100      10 
    2022-12-15 10:58:44    36.5     120      15 

타임테이블형 배열을 다룰 때 유용한 함수들

readtimetable / writetimetable

타임테이블형 배열은 스프레드시트 형식의 파일에서부터 읽어들이거나 스프레드시트 형식으로 출력해야 하는 경우가 왕왕있다. 이럴 때를 대비해 “readtimetable” 혹은 “writetimetable” 함수를 이용할 수 있다. “readtimetable” 을 쓸 때는 엑셀에서 특정 열의 셀 서식이 날짜/시간을 표기하도록 설정되어야 한다. 또, 만약, 어떤 열이 날짜/시간을 표기하는지 지정하지 않고 “readtimetable” 함수를 사용한다면 날짜/시간을 표기하도록 설정한 가장 첫번째 열을 ‘rowTimes’ 변수로 지정한다.


아래는 MATLAB에 포함되어 있는 ‘outages.csv’ 파일을 timetable로 읽어들이는 예시이다.

TT = readtimetable('outages.csv')
TT =

  1468×5 timetable

       OutageTime          Region         Loss     Customers     RestorationTime            Cause       
    ________________    _____________    ______    __________    ________________    ___________________

    2002-02-01 12:18    {'SouthWest'}    458.98    1.8202e+06    2002-02-07 16:50    {'winter storm'   }
    2003-01-23 00:49    {'SouthEast'}    530.14    2.1204e+05                 NaT    {'winter storm'   }
    2003-02-07 21:15    {'SouthEast'}     289.4    1.4294e+05    2003-02-17 08:14    {'winter storm'   }
    2004-04-06 05:44    {'West'     }    434.81    3.4037e+05    2004-04-06 06:10    {'equipment fault'}
    2002-03-16 06:18    {'MidWest'  }    186.44    2.1275e+05    2002-03-18 23:23    {'severe storm'   }

           :                  :            :           :                :                     :         

    2011-11-21 16:51    {'West'     }         0             0    2011-11-21 16:55    {'attack'         }
    2011-01-03 05:52    {'SouthEast'}       NaN    2.7596e+05    2011-01-06 05:25    {'winter storm'   }
    2011-01-02 14:41    {'MidWest'  }    364.24    2.8432e+05    2011-01-04 04:43    {'winter storm'   }
    2013-12-20 19:52    {'SouthEast'}    2.3096        1038.2    2013-12-20 23:29    {'thunder storm'  }
    2011-09-14 11:55    {'SouthEast'}    45.042         11835    2011-09-14 13:28    {'equipment fault'}

	Display all 1468 rows.

“writetimetable”은 아래와 같이 스프레드시트 형식으로 출력하고자 하는 타임테이블과 파일명을 넣어주면 된다.

TT = readtimetable('outages.csv');
writetimetable(TT, 'myfilename.csv')

retime

타임테이블 데이터를 얻어와보면 어떤 경우는 시간이 불규칙하게 찍힌 경우를 종종 볼 수 있다. 이렇게 되면 추후 데이터 분석을 할 때 분석 결과가 헷갈릴 수도 있고, 변환 과정에서 오류가 발생할 수도 있다. 이럴 때는 시간을 resampling 해주고 데이터값은 interpolation 해주어야 한다. 이럴 때 retime 함수를 쓰면 타임테이블에 들어있는 내용물들을 편리하게 규칙적인 시간 간격으로 맞춰줄 수 있다.

가령 아래와 같은 타임테이블을 보면 정확히 한 시간 간격으로 데이터가 기록되지 않은 것을 볼 수 있다.

           Time            Temp    Height    Foo
    ___________________    ____    ______    ___

    2022-12-15 09:40:24      38     100      10 
    2022-12-15 10:58:44    36.5     120      15 
    2022-12-15 12:05:02      37     105      32 

이럴 때 retime 함수를 이용해 가령 매 시 (hour) 기준으로 데이터를 새롭게 리샘플링 및 보간해줄 수 있다.

Time = datetime({...
    '2022-12-15 09:40:24'; ...
    '2022-12-15 10:58:44'; ...
    '2022-12-15 12:05:02'});
Temp = [38; 36.5; 37];
Height = [100; 120; 105];
Foo = [10; 15; 32];

TT = timetable(Time, Temp, Height, Foo);
TT2 = retime(TT, 'hourly', 'linear');


synchronize

온도와 습도를 측정하는데 다른 두 개의 타임테이블에 각각 온도와 습도를 측정했다고 생각해보자. 이럴 때 두 개의 테이블을 하나로 합쳐줄 필요가 있다. 이럴 때 “synchronize” 함수를 이용하면 편리하게 두 타임테이블을 합칠 수 있다.

load firstTT
synchronize(TT1, TT2, 'union', 'linear')
TT1 =

  3×1 timetable

           Time            Temp
    ___________________    ____

    2016-06-06 15:00:00    79.7
    2016-06-06 16:00:00    76.3
    2016-06-06 17:00:00    74.9


TT2 =

  4×1 timetable

           Time            Humidity
    ___________________    ________

    2016-06-06 14:35:48      49.7  
    2016-06-06 15:35:48      52.2  
    2016-06-06 16:35:48      56.7  
    2016-06-06 17:35:48        60  


ans =

  7×2 timetable

           Time             Temp     Humidity
    ___________________    ______    ________

    2016-06-06 14:35:48    81.071       49.7 
    2016-06-06 15:00:00      79.7     50.708 
    2016-06-06 15:35:48    77.671       52.2 
    2016-06-06 16:00:00      76.3     54.014 
    2016-06-06 16:35:48    75.464       56.7 
    2016-06-06 17:00:00      74.9      58.03 
    2016-06-06 17:35:48    74.064         60 
  1. MATLAB에서 사용하는 더 정확한 1년의 길이는 365.2425일이다. 이를 확인해보려면 “days(years(1))”이라고 입력하면 된다. 7319일을 연 단위로 환산했을 때 20.0387년이 나오는 것도 7319/365가 아니라 7319/365.2425와 같이 정확한 1년의 길이로 나누었을때 얻을 수 있는 결과이다.